/****************************************************************************
**
** FontsManager.cpp
**
** Copyright (C) August 2014 Hotride
**
** This program is free software; you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation; either version 2 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
** GNU General Public License for more details.
**
*****************************************************************************
*/
//---------------------------------------------------------------------------
#include "stdafx.h"
//---------------------------------------------------------------------------
TFontsManager::TFontsManager()
: m_SavePixels(false), m_UseHTML(false)
{
	memset(m_UnicodeFontAddress, 0, sizeof(m_UnicodeFontAddress));
	memset(m_UnicodeFontSize, 0, sizeof(m_UnicodeFontSize));
}
//---------------------------------------------------------------------------
TFontsManager::~TFontsManager()
{
	m_Font.clear();
}
//---------------------------------------------------------------------------
void TFontsManager::LoadFonts(DWORD Address, DWORD Size)
{
	PVOID ptr = (PVOID)Address;
	PVOID end = (PVOID)(Address + Size);
	int Index = 0;

	while (ptr < end)
	{
		FONT_DATA fd;
		fd.Header = *(PBYTE)ptr;
		m_Font[Index] = fd;
		ptr = (PVOID)((DWORD)ptr + 1);

		IFOR(i, 0, 224)
		{
			PFONT_HEADER fh = (PFONT_HEADER)ptr;
			ptr = (PVOID)((DWORD)ptr + sizeof(FONT_HEADER));

			m_Font[Index].Chars[i].Width = fh->Width;
			m_Font[Index].Chars[i].Height = fh->Height;

			int bcount = fh->Width * fh->Height * 2;

			if ((PVOID)((DWORD)ptr + bcount) > end) //Bad font file...
			{
				m_Font.erase(Index);

				return;
			}

			m_Font[Index].Chars[i].Data = (PWORD)ptr;

			ptr = (PVOID)((DWORD)ptr + bcount);
		}

		Index++;
	}
}
//---------------------------------------------------------------------------
void TFontsManager::SetUnicodeFontData(int Pos, DWORD Address, DWORD Size)
{
	if (Pos < 0 || Pos >= 20) return;

	m_UnicodeFontAddress[Pos] = Address;
	m_UnicodeFontSize[Pos] = Size;
}
//---------------------------------------------------------------------------
//-----------------------------------ASCII----------------------------------
//---------------------------------------------------------------------------
float TFontsManager::GetFontOffsetY(BYTE Number, BYTE Index)
{
	float OffsY = 0.0f;

	if (!(Index >= 0x41 && Index <= 0x5A) && !(Index >= 0xC0 && Index <= 0xDF) && Index != 0xA8)
	{
		if (Number == 1 || Number == 5 || Number == 9) {}
		else OffsY = 2.0f;
	}
	else if (Index == 0xB8) OffsY = 1.0f;

	return OffsY;
}
//---------------------------------------------------------------------------
int TFontsManager::GetWidthA(BYTE font, const char *str, int len)
{
	if (!len)
	{
		len = strlen(str);
		if (!len) return 0;
	}

	FONT_MAP::iterator it = m_Font.find(font);
	if (it == m_Font.end()) return 0;
	FONT_DATA &fd = (*it).second;

	int TextLength = 0;

	IFOR(i, 0, len)
	{
		BYTE Index = m_FontIndex[(BYTE)str[i]];

		if (Index >= 0xE0) continue;

		TextLength += fd.Chars[Index].Width;
	}

	return TextLength;
}
//---------------------------------------------------------------------------
int TFontsManager::GetHeightA(BYTE font, const char *str, float width, TEXT_ALIGN_TYPE align, WORD flags)
{
	int len = strlen(str);

	if (len < 1) return 0;

	FONT_MAP::iterator it = m_Font.find(font);
	if (it == m_Font.end()) return 0;
	FONT_DATA &fd = (*it).second;

	if (!width) width = GetWidthA(font, str, len);

	PMULTILINES_FONT_INFO info = GetInfoA(font, str, len, align, flags, width);
	if (info == NULL) return 0;
	
	int TextHeight = 0;
	PMULTILINES_FONT_INFO ptr = info;

	while (ptr != NULL)
	{
		info = ptr;

		TextHeight += ptr->MaxHeight;

		ptr = ptr->m_Next;

		info->Data.clear();
		delete info;
	}

	return TextHeight;
}
//---------------------------------------------------------------------------
int TFontsManager::GetHeightA(PMULTILINES_FONT_INFO info)
{
	if (info == NULL) return 0;
	
	int TextHeight = 0;

	while (info != NULL)
	{
		TextHeight += info->MaxHeight;

		info = info->m_Next;
	}

	return TextHeight;
}
//---------------------------------------------------------------------------
string TFontsManager::GetTextByWidthA(BYTE font, const char *str, int len, float width, bool IsCropped)
{
	if (!len) return string("");

	FONT_MAP::iterator it = m_Font.find(font);
	if (it == m_Font.end()) return 0;
	FONT_DATA &fd = (*it).second;

	float WidthOffs = 0.0f;
	if (IsCropped)
	{
		BYTE idx = m_FontIndex[(BYTE)'.'];
		if (idx < 0xE0)
		{
			WidthOffs = (float)(fd.Chars[idx].Width * 3.0f);
		}
	}

	int TextLength = 0;
	string result = "";

	IFOR(i, 0, len)
	{
		BYTE Index = m_FontIndex[(BYTE)str[i]];

		if (Index >= 0xE0) continue;

		if ((int)(TextLength + fd.Chars[Index].Width) > (int)(width - WidthOffs)) break;

		result += str[i];

		TextLength += fd.Chars[Index].Width;
	}

	if (IsCropped) result += "...";

	return result;
}
//---------------------------------------------------------------------------
PMULTILINES_FONT_INFO TFontsManager::GetInfoA(BYTE font, const char *str, int len, TEXT_ALIGN_TYPE align, WORD flags, float width)
{
	FONT_MAP::iterator it = m_Font.find(font);
	if (it == m_Font.end()) return NULL;
	FONT_DATA &fd = (*it).second;

	PMULTILINES_FONT_INFO info = new MULTILINES_FONT_INFO();
	info->Reset();
	info->Align = align;

	PMULTILINES_FONT_INFO ptr = info;

	float IndentionOffset = 0;
	ptr->IndentionOffset = IndentionOffset;
	
	int CharCount = 0;
	int LastSpace = 0;
	int ReadWidth = 0.0f;

	IFOR(i, 0, len)
	{
		char si = str[i];
		if (si == 0x0D || si == '\n')
		{
			if (align == TS_FIXED || align == TS_CROPPED) continue;
			else si = '\n';
		}

		BYTE Index = m_FontIndex[(BYTE)si];
		if (Index >= 0xE0 && si != '\n') continue;

		if (si == ' ')
		{
			LastSpace = i;
			ptr->Width += ReadWidth;
			ReadWidth = 0.0f;
			ptr->CharCount += CharCount;
			CharCount = 0;
		}

		if (ptr->Width + ReadWidth + fd.Chars[Index].Width > width || si == '\n')
		{
			if (LastSpace == ptr->CharStart && !LastSpace && si != '\n') ptr->CharStart = 1;

			if (si == '\n')
			{
				ptr->Width += ReadWidth;
				ptr->CharCount += CharCount;

				LastSpace = i;
						
				if (!ptr->Width) ptr->Width = 1.0f;
				if (!ptr->MaxHeight) ptr->MaxHeight = 14;
						
				ptr->Data.resize(ptr->CharCount);
						
				PMULTILINES_FONT_INFO newptr = new MULTILINES_FONT_INFO();
				newptr->Reset();
					
				ptr->m_Next = newptr;

				ptr = newptr;

				ptr->Align = align;
				ptr->CharStart = i + 1;
						
				ReadWidth = 0.0f;
				CharCount = 0;
				IndentionOffset = 0.0f;
				
				ptr->IndentionOffset = IndentionOffset;

				continue;
			}
			else if (LastSpace + 1 == ptr->CharStart && align != TS_FIXED && align != TS_CROPPED)
			{
				ptr->Width += ReadWidth;
				ptr->CharCount += CharCount;
						
				if (!ptr->Width) ptr->Width = 1.0f;
				if (!ptr->MaxHeight) ptr->MaxHeight = 10;
						
				PMULTILINES_FONT_INFO newptr = new MULTILINES_FONT_INFO();
				newptr->Reset();

				ptr->m_Next = newptr;
					
				ptr = newptr;

				ptr->Align = align;
				ptr->CharStart = i;
				LastSpace = i - 1;
				CharCount = 0;
						
				if (ptr->Align == TS_LEFT && (flags & UOFONT_INDENTION))
				{
					IndentionOffset = 14.0f;
				}
				
				ptr->IndentionOffset = IndentionOffset;
						
				ReadWidth = IndentionOffset;
			}
			else
			{
				if (align == TS_FIXED)
				{
					MULTILINES_FONT_DATA mfd = {si, flags, 0xFFFFFFFF};
					ptr->Data.push_back(mfd);

					ReadWidth += (float)fd.Chars[Index].Width;

					if (fd.Chars[Index].Height > ptr->MaxHeight) ptr->MaxHeight = fd.Chars[Index].Height;
				
					CharCount++;

					ptr->Width += ReadWidth;
					ptr->CharCount += CharCount;
				}

				i = LastSpace + 1;
				si = str[i];
						
				if (!ptr->Width) ptr->Width = 1.0f;
				if (!ptr->MaxHeight) ptr->MaxHeight = 10;
						
				ptr->Data.resize(ptr->CharCount);

				if (align == TS_FIXED || align == TS_CROPPED) break;

				PMULTILINES_FONT_INFO newptr = new MULTILINES_FONT_INFO();
				newptr->Reset();
					
				ptr->m_Next = newptr;

				ptr = newptr;

				ptr->Align = align;
				ptr->CharStart = i;
				CharCount = 0;
						
				if (ptr->Align == TS_LEFT && (flags & UOFONT_INDENTION))
				{
					IndentionOffset = 14.0f;
				}
				
				ptr->IndentionOffset = IndentionOffset;

				ReadWidth = IndentionOffset;
			}
		}
				
		MULTILINES_FONT_DATA mfd = {si, flags, 0xFFFFFFFF};
		ptr->Data.push_back(mfd);

		ReadWidth += (float)fd.Chars[Index].Width;

		if (fd.Chars[Index].Height > ptr->MaxHeight) ptr->MaxHeight = fd.Chars[Index].Height;
				
		CharCount++;
	}

	ptr->Width += ReadWidth;
	ptr->CharCount += CharCount;

	return info;
}
//---------------------------------------------------------------------------
bool TFontsManager::GenerateA(BYTE font, TEXT_TEXTURE_HEADER &th, const char *str, WORD color, float width, TEXT_ALIGN_TYPE align, WORD flags)
{
	if (align == TS_FIXED || align == TS_CROPPED)
	{
		th.Clear();

		if (!width) return false;
		
		int len = strlen(str);
		if (!len) return false;
	
		float real_width = GetWidthA(font, str, len);

		if (real_width > width)
		{
			string newstr = GetTextByWidthA(font, str, len, width, (align == TS_CROPPED));
			return GenerateABase(font, th, newstr.c_str(), color, width, align, flags);
		}
	}
	
	return GenerateABase(font, th, str, color, width, align, flags);
}
//---------------------------------------------------------------------------
bool TFontsManager::GenerateABase(BYTE &font, TEXT_TEXTURE_HEADER &th, const char *str, WORD &color, float &width, TEXT_ALIGN_TYPE &align, WORD &flags)
{
	th.Clear();

	int len = strlen(str);
	if (!len) return false;
	
	FONT_MAP::iterator it = m_Font.find(font);
	if (it == m_Font.end()) return false;
	FONT_DATA &fd = (*it).second;
	
	if (!width) width = GetWidthA(font, str, len);
	if (!width) return false;

	PMULTILINES_FONT_INFO info = GetInfoA(font, str, len, align, flags, width);
	if (info == NULL) return false;
	
	width += 4.0f;
	
	int height = GetHeightA(info);
	
	if (font == 6)
	{
		PMULTILINES_FONT_INFO infoptr = info;

		while (infoptr != NULL)
		{
			if (infoptr->MaxHeight > 7) height -= 7;

			infoptr = infoptr->m_Next;
		}
	}

	if (!height)
	{
		PMULTILINES_FONT_INFO ptr = info;

		while (ptr != NULL)
		{
			info = ptr;

			ptr = ptr->m_Next;
		
			info->Data.clear();
			delete info;
		}

		return false;
	}

	int blocksize = height * width;

	if (m_SavePixels)
	{
		th.Data = new BYTE[blocksize];
		memset(&th.Data[0], 0, blocksize);
	}

	blocksize *= 4;
	PDWORD pData = new DWORD[blocksize];
	memset(&pData[0], 0, blocksize);

	float LineOffsY = 0.0f;

	PMULTILINES_FONT_INFO ptr = info;

	while (ptr != NULL)
	{
		info = ptr;

		th.Lines++;
		
		float w = 0.0f;
		if (ptr->Align == TS_CENTER)
		{
			w = (float)(((width - 10.0f) - ptr->Width) / 2.0f);
			if (w < 0.0f) w = 0.0f;
		}
		else if (ptr->Align == TS_RIGHT)
		{
			w = (float)((width - 10.0f) - ptr->Width);
			if (w < 0.0f) w = 0.0f;
		}
		else if (ptr->Align == TS_LEFT && (flags & UOFONT_INDENTION))
		{
			w = ptr->IndentionOffset;
		}

		int Start = ptr->CharStart;
		int End = ptr->CharStart + ptr->CharCount;
		
		//IFOR(i, Start, End)
		IFOR(i, 0, ptr->Data.size())
		{
			BYTE Index = (BYTE)ptr->Data[i].item;
			float OffsY = GetFontOffsetY(font, Index);

			Index = m_FontIndex[Index];
			if (Index >= 0xE0) continue;

			int DW = fd.Chars[Index].Width;
			int DH = fd.Chars[Index].Height;

			WORD charcolor = color;

			for (int Y = 0; Y < DH; Y++)
			{
				int TestY = Y + ((int)LineOffsY) + ((int)OffsY);

				for (int X = 0; X < DW; X++)
				{
					if ((X + (int)w) >= (int)width) break;

					WORD pic = fd.Chars[Index].Data[(Y * DW) + X];

					if (pic)
					{
						DWORD pcl = 0;

						if (font == 5 || font == 8) pcl = UO->m_ColorManager->GetColor(pic, charcolor);
						else pcl = UO->m_ColorManager->GetPartialHueColor(pic, charcolor);

						int block = (TestY * (int)width) + (X + ((int)w));

						pData[block] = (0xFF << 24) | (GetBValue(pcl) << 16) | (GetGValue(pcl) << 8) | GetRValue(pcl);

						if (m_SavePixels) th.Data[block] = ((pic >> 10) & 0x1F);
					}
				}
			}

			w += (float)DW;
		}

		LineOffsY += ptr->MaxHeight;

		if (font == 6) LineOffsY -= 7;

		ptr = ptr->m_Next;
		
		info->Data.clear();
		delete info;
	}

	th.Width = width;
	th.Height = height;
	UO->m_GL.BindTexture(th.Texture, width, height, pData);

	delete pData;

	return true;
}
//---------------------------------------------------------------------------
void TFontsManager::DrawA(BYTE font, const char *str, WORD color, float X, float Y, float width, TEXT_ALIGN_TYPE align, WORD flags)
{
	TEXT_TEXTURE_HEADER th;

	if (!GenerateA(font, th, str, color, width, align, flags)) return;

	UO->m_GL.Draw(th.Texture, X, Y, th.Width, th.Height);

	th.Clear();
}









//---------------------------------------------------------------------------
//----------------------------------Unicode---------------------------------
//---------------------------------------------------------------------------
int TFontsManager::GetWidthW(BYTE font, const wchar_t *str, int len)
{
	if (font >= 20 || !m_UnicodeFontAddress[font]) return 0;

	if (!len)
	{
		len = lstrlenW(str);
		if (!len) return 0;
	}

	PDWORD Table = (PDWORD)m_UnicodeFontAddress[font];
	int TextLength = 0;

	IFOR(i, 0, len)
	{
		if (Table[str[i]] && Table[str[i]] != 0xFFFFFFFF)
		{
			PBYTE ptr = (PBYTE)((DWORD)Table + Table[str[i]]);
			TextLength += ((char)ptr[0] + (char)ptr[2] + 1.0f);
		}
		else if (str[i] == L' ') TextLength += g_UnicodeSpaceWidth;
	}

	return TextLength;
}
//---------------------------------------------------------------------------
int TFontsManager::GetHeightW(BYTE font, const wchar_t *str, float width, TEXT_ALIGN_TYPE align, WORD flags)
{
	if (font >= 20 || !m_UnicodeFontAddress[font]) return 0;

	int len = lstrlenW(str);

	if (len < 1) return 0;

	if (!width) width = GetWidthW(font, str, len);

	PMULTILINES_FONT_INFO info = GetInfoW(font, str, len, align, flags, width);
	if (info == NULL) return 0;
	
	PDWORD Table = (PDWORD)m_UnicodeFontAddress[font];
	int TextHeight = 0;
	PMULTILINES_FONT_INFO ptr = info;

	while (ptr != NULL)
	{
		info = ptr;

		TextHeight += ptr->MaxHeight;

		ptr = ptr->m_Next;

		info->Data.clear();
		delete info;
	}

	return TextHeight;
}
//---------------------------------------------------------------------------
int TFontsManager::GetHeightW(PMULTILINES_FONT_INFO info)
{
	if (info == NULL) return 0;
	
	int TextHeight = 0;

	while (info != NULL)
	{
		TextHeight += info->MaxHeight;

		info = info->m_Next;
	}

	return TextHeight;
}
//---------------------------------------------------------------------------
wstring TFontsManager::GetTextByWidthW(BYTE font, const wchar_t *str, int len, float width, bool IsCropped)
{
	if (font >= 20 || !m_UnicodeFontAddress[font] || !len) return wstring(L"");

	PDWORD Table = (PDWORD)m_UnicodeFontAddress[font];

	float WidthOffs = 0.0f;
	if (IsCropped)
	{
		wchar_t Dot = '.';
		if (Table[Dot] && Table[Dot] != 0xFFFFFFFF) WidthOffs = (float)(*((PBYTE)((DWORD)Table + Table[Dot] + 2)) * 3.0f);
	}

	int TextLength = 0;
	wstring result = L"";

	IFOR(i, 0, len)
	{
		if (Table[str[i]] && Table[str[i]] != 0xFFFFFFFF)
		{
			PBYTE ptr = (PBYTE)((DWORD)Table + Table[str[i]]);
			int wd = ((char)ptr[0] + (char)ptr[2] + 1.0f);

			if ((int)(TextLength + wd) > (int)(width - WidthOffs)) break;

			result += str[i];

			TextLength += wd;
		}
		else if (str[i] == L' ')
		{
			result += str[i];
			TextLength += g_UnicodeSpaceWidth;
		}
	}

	if (IsCropped) result += L"...";

	return result;
}
//---------------------------------------------------------------------------
PMULTILINES_FONT_INFO TFontsManager::GetInfoW(BYTE font, const wchar_t *str, int len, TEXT_ALIGN_TYPE align, WORD flags, float width)
{
	if (font >= 20 || !m_UnicodeFontAddress[font]) return NULL;
	
	PDWORD Table = (PDWORD)m_UnicodeFontAddress[font];

	PMULTILINES_FONT_INFO info = new MULTILINES_FONT_INFO();
	info->Reset();
	info->Align = align;

	PMULTILINES_FONT_INFO ptr = info;

	float IndentionOffset = 0;
	
	ptr->IndentionOffset = IndentionOffset;
	
	int CharCount = 0;
	int LastSpace = 0;
	int ReadWidth = 0.0f;

	TEXT_ALIGN_TYPE current_align = align;
	DWORD current_flags = flags;

	IFOR(i, 0, len)
	{
		start_check_unifont:
		wchar_t si = str[i];

		int copylen = len - i;

		if (m_UseHTML && si == L'<' && copylen > 2)
		{
			wchar_t lstr[10] = {0};

			if (copylen > 10) copylen = 10;

			memcpy(&lstr[0], &str[i], copylen * 2);

			_wcslwr(lstr);

			if (!memcmp(lstr, L"<center>", 16))
			{
				current_align = TS_CENTER;
				i += 7;
				si = L'\n';
			}
			else if (!memcmp(lstr, L"</center>", 18))
			{
				current_align = align;
				i += 8;
				si = L'\n';
			}
			else if (!memcmp(lstr, L"<br>", 8))
			{
				i += 3;
				si = L'\n';
			}
			else if (!memcmp(lstr, L"<i>", 6))
			{
				if (!(current_flags & UOFONT_ITALIC)) current_flags |= UOFONT_ITALIC;
				i += 3;
				goto start_check_unifont;
			}
			else if (!memcmp(lstr, L"</i>", 8))
			{
				if (current_flags & UOFONT_ITALIC) current_flags ^= UOFONT_ITALIC;
				i += 4;
				goto start_check_unifont;
			}
			else if (!memcmp(lstr, L"<b>", 6))
			{
				if (!(current_flags & UOFONT_SOLID)) current_flags |= UOFONT_SOLID;
				i += 3;
				goto start_check_unifont;
			}
			else if (!memcmp(lstr, L"</b>", 8))
			{
				if (current_flags & UOFONT_SOLID) current_flags ^= UOFONT_SOLID;
				i += 4;
				goto start_check_unifont;
			}
			else if (!memcmp(lstr, L"<div ", 10))
			{
			}
			else if (!memcmp(lstr, L"</div ", 12))
			{
			}
		}

		if (si == 0x000D || si == L'\n')
		{
			if (current_align == TS_FIXED || current_align == TS_CROPPED) si = L'';
			else si = L'\n';
		}

		if ((!Table[si] || Table[si] == 0xFFFFFFFF) && si != L' ' && si != L'\n') continue;

		PBYTE data = (PBYTE)((DWORD)Table + Table[si]);

		if (si == L' ')
		{
			LastSpace = i;
			ptr->Width += ReadWidth;
			ReadWidth = 0.0f;
			ptr->CharCount += CharCount;
			CharCount = 0;
		}

		if (ptr->Width + ReadWidth + ((char)data[0] + (char)data[2]) > width || si == L'\n')
		{
			if (LastSpace == ptr->CharStart && !LastSpace && si != L'\n') ptr->CharStart = 1;

			if (si == L'\n')
			{
				ptr->Width += ReadWidth;
				ptr->CharCount += CharCount;

				LastSpace = i;
						
				if (!ptr->Width) ptr->Width = 1.0f;
				if (!ptr->MaxHeight) ptr->MaxHeight = 14;
						
				ptr->Data.resize(ptr->CharCount);
						
				PMULTILINES_FONT_INFO newptr = new MULTILINES_FONT_INFO();
				newptr->Reset();
					
				ptr->m_Next = newptr;

				ptr = newptr;

				ptr->Align = current_align;
				ptr->CharStart = i + 1;
						
				ReadWidth = 0.0f;
				CharCount = 0;
				IndentionOffset = 0.0f;
				
				ptr->IndentionOffset = IndentionOffset;

				continue;
			}
			else if (LastSpace + 1 == ptr->CharStart && current_align != TS_FIXED && current_align != TS_CROPPED)
			{
				ptr->Width += ReadWidth;
				ptr->CharCount += CharCount;
						
				if (!ptr->Width) ptr->Width = 1.0f;
				if (!ptr->MaxHeight) ptr->MaxHeight = 10;
						
				PMULTILINES_FONT_INFO newptr = new MULTILINES_FONT_INFO();
				newptr->Reset();

				ptr->m_Next = newptr;
					
				ptr = newptr;

				ptr->Align = current_align;
				ptr->CharStart = i;
				LastSpace = i - 1;
				CharCount = 0;
						
				if (ptr->Align == TS_LEFT && (current_flags & UOFONT_INDENTION))
				{
					IndentionOffset = 14.0f;
				}
				
				ptr->IndentionOffset = IndentionOffset;
						
				ReadWidth = IndentionOffset;
			}
			else
			{
				if (current_align == TS_FIXED)
				{
					MULTILINES_FONT_DATA mfd = {si, current_flags, 0xFFFFFFFF};
					ptr->Data.push_back(mfd);

					ReadWidth += (float)((float)(char)data[0] + (float)(char)data[2] + 1.0f);

					if (((char)data[1] + (char)data[3]) > ptr->MaxHeight) ptr->MaxHeight = ((char)data[1] + (char)data[3]);
				
					CharCount++;

					ptr->Width += ReadWidth;
					ptr->CharCount += CharCount;
				}

				i = LastSpace + 1;
				si = str[i];
						
				if (!ptr->Width) ptr->Width = 1.0f;
				if (!ptr->MaxHeight) ptr->MaxHeight = 10;
						
				ptr->Data.resize(ptr->CharCount);

				if (current_align == TS_FIXED || current_align == TS_CROPPED) break;

				PMULTILINES_FONT_INFO newptr = new MULTILINES_FONT_INFO();
				newptr->Reset();
					
				ptr->m_Next = newptr;

				ptr = newptr;

				ptr->Align = current_align;
				ptr->CharStart = i;
				CharCount = 0;
						
				if (ptr->Align == TS_LEFT && (current_flags & UOFONT_INDENTION))
				{
					IndentionOffset = 14.0f;
				}
				
				ptr->IndentionOffset = IndentionOffset;

				ReadWidth = IndentionOffset;
			}
		}
				
		MULTILINES_FONT_DATA mfd = {si, current_flags, 0xFFFFFFFF};
		ptr->Data.push_back(mfd);

		if (si == L' ')
		{
			ReadWidth += g_UnicodeSpaceWidth;

			if (!ptr->MaxHeight) ptr->MaxHeight = 5;
		}
		else
		{
			ReadWidth += (float)((float)(char)data[0] + (float)(char)data[2] + 1.0f);

			if (((char)data[1] + (char)data[3]) > ptr->MaxHeight) ptr->MaxHeight = ((char)data[1] + (char)data[3]);
		}

		CharCount++;
	}

	ptr->Width += ReadWidth;
	ptr->CharCount += CharCount;

	return info;
}
//---------------------------------------------------------------------------
bool TFontsManager::GenerateW(BYTE font, TEXT_TEXTURE_HEADER &th, const wchar_t *str, WORD color, BYTE cell, float width, TEXT_ALIGN_TYPE align, WORD flags)
{
	if (align == TS_FIXED || align == TS_CROPPED)
	{
		th.Clear();

		if (!width) return false;
		
		int len = lstrlenW(str);
		if (!len) return false;
	
		float real_width = GetWidthW(font, str, len);

		if (real_width > width)
		{
			wstring newstr = GetTextByWidthW(font, str, len, width, (align == TS_CROPPED));
			return GenerateWBase(font, th, newstr.c_str(), color, cell, width, align, flags);
		}
	}
	
	return GenerateWBase(font, th, str, color, cell, width, align, flags);
}
//---------------------------------------------------------------------------
bool TFontsManager::GenerateWBase(BYTE &font, TEXT_TEXTURE_HEADER &th, const wchar_t *str, WORD &color, BYTE &cell, float &width, TEXT_ALIGN_TYPE &align, WORD &flags)
{
	if (font >= 20 || !m_UnicodeFontAddress[font]) return false;

	int len = lstrlenW(str);
	if (!len) return false;
	
	if (!width) width = GetWidthW(font, str, len);
	if (!width) return false;

	PMULTILINES_FONT_INFO info = GetInfoW(font, str, len, align, flags, width);
	if (info == NULL) return false;
	
	width += 4.0f;
	
	int height = GetHeightW(info);

	if (!height)
	{
		PMULTILINES_FONT_INFO ptr = info;

		while (ptr != NULL)
		{
			info = ptr;

			ptr = ptr->m_Next;
		
			info->Data.clear();
			delete info;
		}

		return false;
	}

	height += 4.0f;

	int blocksize = height * width;

	if (m_SavePixels)
	{
		th.Data = new BYTE[blocksize];
		memset(&th.Data[0], 0, blocksize);
	}

	blocksize *= 4;
	PDWORD pData = new DWORD[blocksize];
	memset(&pData[0], 0, blocksize);

	PDWORD Table = (PDWORD)m_UnicodeFontAddress[font];

	float LineOffsY = 1.0f;

	PMULTILINES_FONT_INFO ptr = info;

	DWORD datacolor = 0;
	if (/*m_UseHTML &&*/ color == 0xFFFF) datacolor = 0xFFFFFFFE;
	else
	{
		datacolor = UO->m_ColorManager->GetPolygoneColor(cell, color);
		datacolor = (0xFF << 24) | (GetBValue(datacolor) << 16) | (GetGValue(datacolor) << 8) | GetRValue(datacolor);
	}

	bool IsItalic = (flags & UOFONT_ITALIC);
	bool IsSolid = (flags & UOFONT_SOLID);
	bool IsBlackBorder = (flags & UOFONT_BLACK_BORDER);
	DWORD BlackColor = 0xFF010101;

	while (ptr != NULL)
	{
		info = ptr;

		th.Lines++;
		
		float w = 0.0f;
		if (ptr->Align == TS_CENTER)
		{
			w = (float)(((width - 10.0f) - ptr->Width) / 2.0f);
			if (w < 0.0f) w = 0.0f;
		}
		else if (ptr->Align == TS_RIGHT)
		{
			w = (float)((width - 10.0f) - ptr->Width);
			if (w < 0.0f) w = 0.0f;
		}
		else if (ptr->Align == TS_LEFT && (flags & UOFONT_INDENTION))
		{
			w = ptr->IndentionOffset;
		}

		int Start = ptr->CharStart;
		int End = ptr->CharStart + ptr->CharCount;

		//IFOR(i, Start, End)
		IFOR(i, 0, ptr->Data.size())
		{
			wchar_t si = ptr->Data[i].item;

			/*if (m_UseHTML)
			{
				if (i >= ptr->Data.size()) break;
				si = ptr->Data[i].item;
			}*/

			if ((!Table[si] || Table[si] == 0xFFFFFFFF) && si != L' ') continue;

			PBYTE data = (PBYTE)((DWORD)Table + Table[si]);

			float OffsX = 0.0f;
			float OffsY = 0.0f;
			float DW = 0.0f;
			float DH = 0.0f;

			if (si == L' ')
			{
				OffsX = 0.0f;
				DW = g_UnicodeSpaceWidth;
			}
			else
			{
				OffsX = (float)(char)data[0] + 1.0f;
				OffsY = (float)(char)data[1];
				DW = (float)(char)data[2];
				DH = (float)(char)data[3];

				data += 4;
			}
			
			if (si != L' ')
			{
				DWORD charcolor = datacolor;

				if (m_UseHTML && i < ptr->Data.size())
				{
					IsItalic = (ptr->Data[i].flags & UOFONT_ITALIC);
					IsSolid = (ptr->Data[i].flags & UOFONT_SOLID);
					IsBlackBorder = (ptr->Data[i].flags & UOFONT_BLACK_BORDER);

					if (ptr->Data[i].color != 0xFFFFFFFF) charcolor = ptr->Data[i].color;
				}
				
				int ScanlineCount = ((DW - 1) / 8) + 1;

				IFOR(y, 0, DH)
				{
					PBYTE scanlines = data;
					data += ScanlineCount;

					int ItalicOffset = 0;
					if (IsItalic) ItalicOffset = (int)((float)(DH - y) / g_ItalicFontKoeff);

					int TestY = ((int)OffsY) + ((int)LineOffsY) + y;

					int TestX = ((int)w + (int)OffsX + ItalicOffset);

					for (int c = 0; c < ScanlineCount; c++)
					{
						for (int j = 0; j < 8; j++)
						{
							int x = (c * 8) + j;
							if (x >= DW) break;

							int NowX = TestX + x;

							if (NowX >= width) break;

							BYTE cl = scanlines[c] & (1 << (7 - j));
						
							int Block = (TestY * (int)width) + NowX;

							if (cl)
							{
								pData[Block] = charcolor;
								if (m_SavePixels) th.Data[Block] = cell;
							}
						}
					}
				}

				if (IsSolid)
				{
					DWORD SolidColor = BlackColor;
					if (SolidColor == charcolor) SolidColor++;
					
					int MinXOk = (((int)w + (int)OffsX) > 0) ? -1 : 0;
					int MinYOk = (((int)OffsY) + ((int)LineOffsY) > 0) ? -1 : 0;
					int MaxXOk = (((int)w + (int)OffsX + (int)DW) < (int)width) ? 1 : 0;
					int MaxYOk = (((int)OffsY) + ((int)LineOffsY + (int)DH) < (int)height) ? 1 : 0;
					
					MaxXOk += (int)DW;
					MaxYOk += (int)DH;

					IFOR(CY, MinYOk, MaxYOk)
					{
						int ItalicOffset = 0;
						if (IsItalic && CY >=0 && CY < (int)DH) ItalicOffset = (int)((float)(DH - CY) / g_ItalicFontKoeff);

						int TestY = ((int)OffsY) + ((int)LineOffsY) + CY;
					
						IFOR(CX, MinXOk, MaxXOk)
						{
							int TestX = (CX + (int)w + (int)OffsX + ItalicOffset);

							if (TestX >= width) break;

							int Block = (TestY * (int)width) + TestX;

							if (!pData[Block] && pData[Block] != SolidColor)
							{
								int StartX = (CX > 0) ? -1 : 0;
								int StartY = (CY > 0) ? -1 : 0;
								int EndX = (CX < DW - 1) ? 2 : 1;
								int EndY = (CY < DH - 1) ? 2 : 1;

								if (EndX == 2 && (TestX + 1) >= width) EndX--;
					
								bool Passed = false;

								IFOR(x, StartX, EndX)
								{
									int NowX = TestX + x;

									IFOR(y, StartY, EndY)
									{
										int TestBlock = ((TestY + y) * (int)width) + NowX;

										if (pData[TestBlock] && pData[TestBlock] != SolidColor)
										{
											pData[Block] = SolidColor;
											if (m_SavePixels) th.Data[Block] = cell;
											Passed = true;
											break;
										}
									}

									if (Passed) break;
								}
							}
						}
					}

					IFOR(CY, 0, (int)DH)
					{
						int ItalicOffset = 0;
						if (IsItalic) ItalicOffset = (int)((float)(DH - CY) / g_ItalicFontKoeff);

						int TestY = ((int)OffsY) + ((int)LineOffsY) + CY;
					
						IFOR(CX, 0, (int)DW)
						{
							int TestX = (CX + (int)w + (int)OffsX + + ItalicOffset);

							if (TestX >= width) break;

							int Block = (TestY * (int)width) + TestX;

							if (pData[Block] == SolidColor)
							{
								pData[Block] = charcolor;
								if (m_SavePixels) th.Data[Block] = cell;
							}
						}
					}
				}

				if (IsBlackBorder && BlackColor != charcolor)
				{
					int MinXOk = (((int)w + (int)OffsX) > 0) ? -1 : 0;
					int MinYOk = (((int)OffsY) + ((int)LineOffsY) > 0) ? -1 : 0;
					int MaxXOk = (((int)w + (int)OffsX + (int)DW) < (int)width) ? 1 : 0;
					int MaxYOk = (((int)OffsY) + ((int)LineOffsY + (int)DH) < (int)height) ? 1 : 0;
					
					MaxXOk += (int)DW;
					MaxYOk += (int)DH;

					IFOR(CY, MinYOk, MaxYOk)
					{
						int ItalicOffset = 0;
						if (IsItalic && CY >=0 && CY < (int)DH) ItalicOffset = (int)((float)(DH - CY) / g_ItalicFontKoeff);

						int TestY = ((int)OffsY) + ((int)LineOffsY) + CY;
					
						IFOR(CX, MinXOk, MaxXOk)
						{
							int TestX = (CX + (int)w + (int)OffsX + ItalicOffset);

							if (TestX >= width) break;

							int Block = (TestY * (int)width) + TestX;

							if (!pData[Block] && pData[Block] != BlackColor)
							{
								int StartX = (CX > 0) ? -1 : 0;
								int StartY = (CY > 0) ? -1 : 0;
								int EndX = (CX < DW - 1) ? 2 : 1;
								int EndY = (CY < DH - 1) ? 2 : 1;

								if (EndX == 2 && (TestX + 1) >= width) EndX--;
					
								bool Passed = false;

								IFOR(x, StartX, EndX)
								{
									int NowX = TestX + x;

									IFOR(y, StartY, EndY)
									{
										int TestBlock = ((TestY + y) * (int)width) + NowX;

										if (pData[TestBlock] && pData[TestBlock] != BlackColor)
										{
											pData[Block] = BlackColor;
											if (m_SavePixels) th.Data[Block] = cell;
											Passed = true;
											break;
										}
									}

									if (Passed) break;
								}
							}
						}
					}
				}

				w += (DW + OffsX);
			}
			else if (si == L' ') w += g_UnicodeSpaceWidth;
		}

		LineOffsY += ptr->MaxHeight;

		ptr = ptr->m_Next;
		
		info->Data.clear();
		delete info;
	}

	th.Width = width;
	th.Height = height;
	UO->m_GL.BindTexture(th.Texture, width, height, pData);

	delete pData;

	return true;
}
//---------------------------------------------------------------------------
void TFontsManager::DrawW(BYTE font, const wchar_t *str, WORD color, float X, float Y, BYTE cell, float width, TEXT_ALIGN_TYPE align, WORD flags)
{
	TEXT_TEXTURE_HEADER th;

	if (!GenerateW(font, th, str, color, cell, width, align, flags)) return;

	UO->m_GL.Draw(th.Texture, X, Y, th.Width, th.Height);

	th.Clear();
}
//---------------------------------------------------------------------------